<%#
kind: snippet
name: kickstart_kernel_options
model: ProvisioningTemplate
snippet: true
description: |
  The list of kernel options for initrd appended to the bootloader kernel line on Red Hat compatible distributions.
  Typically renders to a string with the url where to fetch the OS installer answer files, e.g.
    network ksdevice=bootif ks.device=bootif BOOTIF=01-52-54-00-8b-a3-86 inst.ks=http://foreman.example.com/unattended/provision inst.ks.sendmac ip=dhcp nameserver=192.168.122.1
  Custom options can be added by setting the array parameter kickstart_kernel_custom_options
-%>
<%
  rhel_compatible = @host.operatingsystem.family == 'Redhat' && @host.operatingsystem.name != 'Fedora'
  is_fedora = @host.operatingsystem.name == 'Fedora'
  os_major = @host.operatingsystem.major.to_i
  os_minor = @host.operatingsystem.minor.to_i
  major = @host.operatingsystem.major.to_i
  hostname = @host.name
  iface = @host.provision_interface
  mac = @host.provision_interface.mac
  subnet4 = iface.subnet
  subnet6 = iface.subnet6
  dual_stack_fallback = host_param('dual_stack_provision_fallback')

  custom_options = [host_param('kickstart_kernel_custom_options')].flatten.compact
  options = []

  if rhel_compatible && os_major < 8
    # replaced with BOOTIF in EL8+
    options.push("network", "ksdevice=bootif", "ks.device=bootif")
  end

  if mac
    # hardware type is always 01 (ethernet) unless specified otherwise
    options.push("BOOTIF=#{host_param("hardware_type", "01")}-#{mac.gsub(':', '-')}")
  end

  # tell Anaconda what to pass off to kickstart server
  # both current and legacy syntax provided
  if (is_fedora && os_major >= 33) || (rhel_compatible && os_major >= 8)
    if subnet4 && !subnet4.dhcp_boot_mode?
      options.push("inst.ks=#{foreman_url('provision', static: '1').gsub("&", "\\\\&")}")
    elsif subnet6 && !subnet6.dhcp_boot_mode?
      options.push("inst.ks=#{foreman_url('provision', static6: '1').gsub("&", "\\\\&")}")
    else
      options.push("inst.ks=#{foreman_url('provision').gsub("&", "\\\\&")}", "inst.ks.sendmac")
    end
  else
    if subnet4 && !subnet4.dhcp_boot_mode?
      options.push("ks=#{foreman_url('provision', static: '1').gsub("&", "\\\\&")}")
    elsif subnet6 && !subnet6.dhcp_boot_mode?
      options.push("ks=#{foreman_url('provision', static6: '1').gsub("&", "\\\\&")}")
    else
      options.push("ks=#{foreman_url('provision').gsub("&", "\\\\&")}", "kssendmac", "ks.sendmac")
    end
  end

  # networking credentials
  if subnet4 && subnet6
    if dual_stack_fallback == 'IPv4'
      subnet6 = false
    elsif dual_stack_fallback == 'IPv6'
      subnet4 = false
    else
      raise("Dual-stack provisioning not supported, set parameter 'dual_stack_provision_fallback'")
    end
  end
  options.push("dualstack!") if subnet4 && subnet6
  if subnet4 && subnet4.dhcp_boot_mode?
    options.push("ip=::::#{hostname}::dhcp") if rhel_compatible && major >= 7
  elsif subnet4 && !subnet4.dhcp_boot_mode?
    if rhel_compatible && major < 7
      dns_servers = subnet4.dns_servers.join(',')
      if @ipxe_net
        options.push('ip=${netX/ip} netmask=${netX/netmask} gateway=${netX/gateway} dns=${dns}')
      else
        options.push("ip=#{iface.ip}", "netmask=#{subnet4.mask}", "gateway=#{subnet4.gateway}", "dns=#{dns_servers}")
      end
    else
      if @ipxe_net
        options.push("ip=${netX/ip}::${netX/gateway}:${netX/netmask}:#{hostname}:#{iface.identifier}:none nameserver=${dns}")
      else
        options.push("ip=#{iface.ip}::#{subnet4.gateway}:#{subnet4.mask}:#{hostname}:#{iface.identifier}:none")
      end
    end
  elsif subnet6 && subnet6.dhcp_boot_mode? && rhel_compatible && major >= 7
    options.push("ip=::::#{hostname}::auto6")
  elsif subnet6 && !subnet6.dhcp_boot_mode?
    raise("Static IPv6 provisioning works on RHEL7 or newer") if rhel_compatible && major < 7
    gw = subnet6.gateway ? "[#{subnet6.gateway}]" : ""
    options.push("ip=[#{iface.ip6}]::#{gw}:#{subnet6.cidr}:#{hostname}:#{iface.identifier}:none")
  end

  # nameservers - must be present even for DHCP subnets because of bug:
  # https://bugzilla.redhat.com/show_bug.cgi?id=1795276
  if subnet4 && !@ipxe_net
    subnet4.dns_servers.each { |server|
      options.push("nameserver=#{server}")
    }
  elsif subnet6 && !@ipxe_net
    subnet6.dns_servers.each { |server|
      options.push("nameserver=#{server}")
    }
  end

  # bond
  if iface.bond? && rhel_compatible && os_major >= 6
    bond_slaves = iface.attached_devices_identifiers.join(',')
    options.push("bond=#{iface.identifier}:#{bond_slaves}:mode=#{iface.mode},#{iface.bond_options.tr(' ', ',')}")
  end

  # VLAN (only on physical is recognized)
  if iface.virtual? && iface.tag.present? && iface.attached_to.present?
    if iface.attached_to.match 'bond'
      @host.bond_interfaces.each do |bond_interface|
          if bond_interface.identifier == iface.attached_to
              bond_slaves = bond_interface.attached_devices_identifiers.join(',')
              options.push("bond=#{bond_interface.identifier}:#{bond_slaves}:mode=#{bond_interface.mode},#{bond_interface.bond_options.tr(' ', ',')}")
          end
      end
    end
    if (is_fedora && os_major < 17) || (rhel_compatible && os_major < 7)
      options.push("vlanid=#{iface.tag}")
    else
      options.push("vlan=#{iface.attached_to}.#{iface.tag}:#{iface.attached_to}")
    end
  end

  if host_param('kickstart_liveimg')
    options.push("inst.stage2=#{medium_uri}")
  end

  # S390x architecture has a different stage two image:
  # https://access.redhat.com/solutions/4206591
  if @host.architecture.to_s.match(/s390x?/i)
    options.push("inst.cmdline")
    options.push("inst.repo=#{medium_uri}")
  end

  # FIPS
  if !is_fedora && os_major >= 7 && host_param_true?('fips_enabled')
    options.push('fips=1')
  end

  nic_delay = subnet4&.nic_delay || subnet6&.nic_delay
  if nic_delay && rhel_compatible && major >= 7
    ["dhcp", "iflink", "ifup", "route", "ipv6dad", "ipv6auto", "carrier"].each do |type|
      options.push("rd.net.timeout.#{type}=#{nic_delay}")
    end
  elsif nic_delay
    options.push("nicdelay=#{nic_delay} linksleep=#{nic_delay}")
  end

  # add user specified custom options if specified (noop if not)
  options.concat(custom_options)
-%>
<%# do not add newline after the next line %>
<%= options.join(' ') -%>
